/*
 * CombinedAnimator.java
 *
 * Created on December 6, 2007, 1:31 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package simfinal;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Cone;
import com.jme.scene.shape.Sphere;

/**
 *
 * @author Eric Drucker
 */
public class CombinedAnimator extends SimpleGame
{
	
	private Cone [] rocketObjects = new Cone[FinalDATA.POPSIZE];
        private Rocket []  rockets = new Rocket[FinalDATA.POPSIZE];
        private Vector3f [] rocketPositions = new Vector3f[FinalDATA.POPSIZE];
        
        final int planetCount = 3;
        private Sphere [] planetObjects = new Sphere[planetCount];        
        private Particle [] planets = new Particle[planetCount];
        private Vector3f [] planetPositions = new Vector3f[planetCount];
        private Particle [] backup = new Particle[planetCount]; 
        
	private Nbody physicsEngine =  null;
        private final double eps = 0.0001;
        private final double deltaT = 0.025;
	
	private FinalPopulation population = new FinalPopulation();
	
	/** Creates a new instance of CombinedAnimator */
	public CombinedAnimator() 
	{
	}
	
	public static void main(String[] args)
	{
		CombinedAnimator app = new CombinedAnimator();
		// Signal to show properties dialog
		app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
		app.start(); // Start the program
	}

	protected void simpleInitGame()
	{
            System.out.println("init");
            
               planets[0] = new Particle(20, new Vector(0.0,0.0,0.0), new Vector(0.0,0.0,0.0));
                planets[1] = new Particle(0.1, new Vector(-10,0,0.0), new Vector(0,.7f,0));
                planets[2] = new Particle(0.01, new Vector(10,0,0), new Vector(0,1,0));
                
                backup[0] = planets[0];
                backup[1] = planets[1];
                backup[2] = planets[2];
                
                //planets[0].getMass() for size of radius?
		planetObjects[0] = new Sphere("planet1",10,10,1);
		planetObjects[1] = new Sphere("planet2",10,10,0.1f);
		planetObjects[2] = new Sphere("planet3",10,10,0.2f);             
		
		planetObjects[0].setSolidColor(ColorRGBA.green);
		planetObjects[1].setSolidColor(ColorRGBA.blue);
		planetObjects[2].setSolidColor(ColorRGBA.red);
		
		planetObjects[0].setLocalTranslation(planets[0].getPosition());
		planetObjects[1].setLocalTranslation(planets[1].getPosition());
		planetObjects[2].setLocalTranslation(planets[2].getPosition());
		
		// Do bounds for the sphere, but we'll use a BoundingBox this time
		planetObjects[0].setModelBound(new BoundingBox());
		planetObjects[1].setModelBound(new BoundingBox());
		planetObjects[2].setModelBound(new BoundingBox());
		
		planetObjects[0].updateModelBound();
		planetObjects[1].updateModelBound();
		planetObjects[2].updateModelBound();
		
		rootNode.attachChild(planetObjects[0]); // Put it in the scene graph
		rootNode.attachChild(planetObjects[1]); // Put it in the scene graph
		rootNode.attachChild(planetObjects[2]); // Put it in the scene graph
		
		//create rockets
		for(int i = 0; i < rocketObjects.length; i++)
		{
			rocketObjects[i] = new Cone("rocketObject" + i,10,10,.1f,1);
			rocketObjects[i].setLocalTranslation(0, 2, 2);
			///set bounding box
			rocketObjects[i].setModelBound(new BoundingBox());
			rocketObjects[i].updateModelBound();
			//set color
			rocketObjects[i].setSolidColor(ColorRGBA.cyan);
			//attach them to root node
			rootNode.attachChild(rocketObjects[i]);
			
			rocketObjects[i].addController(new RocketController(rocketObjects[i],i));
		}
		
		population = new FinalPopulation();
		FinalChromosome[] individuals = population.getIndividuals();
		
		for(int i = 0; i < rockets.length; i++)
		{
                        //this Vector should have the same values as the translation vector for the cones (100, 100, -100)
			rockets[i] = new Rocket(individuals[i].getAlleles(), new Vector(0,2,2));
		}
		
                //physicsEngine = new Nbody(planets[],rockets[], 0.00001);
                physicsEngine = new Nbody(Merge(planets,rockets),eps);
                Split(physicsEngine.advance(deltaT));
                
		rootNode.addController(new PlanetController());
		rootNode.addController(new Evolver());
		
	}
        
        private Particle[] Merge(Particle [] pa,Rocket[] ra)
        {
            Particle[] particles = new Particle[pa.length+ra.length];
            
            for(int i=0; i < pa.length; i++)
                particles[i]=pa[i];
            
            for(int i=0; i < ra.length; i++)
                particles[i+pa.length]=ra[i];
            
            return particles;
        }
        
        public void Split(Particle[] system)
        {
            for(int i=0; i < planetCount; i++)
                planetPositions[i]=system[i].getPosition();
            
            for(int i=0; i < FinalDATA.POPSIZE; i++)
                rocketPositions[i]=system[i+planetCount].getPosition();
        }
	
	class Evolver extends Controller
	{
		public Evolver()
		{
		}
		
		public void update(float time)
		{
			boolean allEmpty = true;
			for(int j = 0; j < rockets.length; j++)
			{
				if(!rockets[j].isEmpty())
				{
					allEmpty = false;
				}
			}
			
			if(allEmpty)
			{
				FinalChromosome[] individuals = population.getIndividuals();
				//set fitnesses
				for(int i = 0; i < individuals.length; i++)
				{
					individuals[i].setFitness(rocketObjects[i].getLocalTranslation().y);
					
	//System.out.println("BEST FITNESS 2: " + rocketObjects[i].getLocalTranslation().y);
				}
				
				//evolve population
				population.evolve();
				
				//reset rockets
				for(int i = 0; i < rocketObjects.length; i++)
				{
					rocketObjects[i].setLocalTranslation(0,0,0);
				}
				
				
				
				
				FinalChromosome[] newIndividuals = population.getIndividuals();
				//create rockets with new strings
				for(int i = 0; i < rockets.length; i++)
				{
					rockets[i] = new Rocket(newIndividuals[i].getAlleles(), new Vector(0,2,2));
				}
                                
                                planets[0] = new Particle(backup[0]);
                                planets[1] = new Particle(backup[1]);
                                planets[2] = new Particle(backup[2]);
                                
                                physicsEngine = new Nbody(Merge(planets,rockets),eps);
				//restart animation
                                
				
			}
		}
	}
		
		
	
	class RocketController extends Controller
	{
		TriMesh shape;
		int rocketNum;
		
		public RocketController(TriMesh shape, int rocketNum)
		{
			this.shape = shape;
			this.rocketNum = rocketNum;
		}
		
		public void update(float time)
		{
			Quaternion q2 = new Quaternion();
			rockets[rocketNum].Fire();
                        //shape.setLocalTranslation(rocketPositions[rocketNum]);
                        shape.setLocalTranslation(rockets[rocketNum].getPosition());
			q2.fromAngleAxis(rockets[rocketNum].getRotation(), new Vector3f(0,1,0));           
			shape.setLocalRotation(q2);
		}	
	}
	
	class PlanetController extends Controller
	{
            public PlanetController()
            {
                
            }
            
            public void update(float time)
            {
                Split(physicsEngine.advance(deltaT));
                
                for(int i = 0; i < planetCount; i++)
                {
                    
                    planetObjects[i].setLocalTranslation(planetPositions[i]);
                    //planetObjects[i].setLocalTranslation(planets[i].x0.toVector3f());
                    //System.out.println("pPos "+ i + " " + planetPositions[i]);
                }
                
                
                
                //This command needs to be run between each generation
                //physicsEngine = new Nbody(Merge(planets,rockets),0.0001);

                //This command needs to be run between each fire() call
            }
		
	}
	
}
